package dti;

import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import common.BitManager;
import common.Constants;
import common.IndexWordUtil;
import common.StopWordFile;
import common.WordHistogram;

/**
 * Main algorithm class. Parses all input files, pulling word tokens from the
 * input based on break characters. Also strips out stop words and processes
 * remaining words. Processed words are written to a BitManager and saved to
 * disk.
 *  
 * @author Zachary M. Allen
 * @author Andrew Bernard
 */
public class DTIIndex {
  /** List of IndexedFile objects */
  private List filesToIndex = null;

  private IndexWordUtil wordUtil;  

  /** File object representing stop word file */
  private StopWordFile stopWordFile = null;

  private char[] breakChars;

  public DTIIndex(Options options) throws IOException {
    filesToIndex = options.getFilesToIndex();
    breakChars = options.getBreakChars();
    if(breakChars != null)
      this.stopWordFile = new StopWordFile(options.getStopWordFile(), breakChars);
    this.wordUtil = new IndexWordUtil(breakChars);
  }

  /**
   * This is the main algorithm method. It parses the input and creates the
   * index file.
   * 
   * @throws IOException Thrown if an error reading from input occurs
   */
  public String write() throws IOException {
    BitManager bm = new BitManager();

    /* MAIN HEADER INFORMATION */
    /* Add date and time of creation */
    GregorianCalendar gc = new GregorianCalendar();
    gc.setTimeInMillis(System.currentTimeMillis());
    bm.addBits((1 + gc.get(Calendar.MONTH)), Constants.MONTH_BITS);
    bm.addBits(gc.get(Calendar.DATE), Constants.DATE_BITS);
    bm.addBits(gc.get(Calendar.YEAR), Constants.YEAR_BITS);
    bm.addBits(gc.get(Calendar.HOUR_OF_DAY), Constants.HOUR_BITS);
    bm.addBits(gc.get(Calendar.MINUTE), Constants.MINUTE_BITS);
    bm.addBits(gc.get(Calendar.SECOND), Constants.SECOND_BITS);

    /* Add break characters used in creation */
    for (int j = Constants.FIRST_CUSTOM_BC; j <= Constants.LAST_CUSTOM_BC; j++) {
      if (wordUtil.isBreakCharacter(j)) {
        bm.addBits(1, 1);
      } else {
        bm.addBits(0, 1);
      }
    }

    /* Add stop file used, if any */
    if (stopWordFile == null) {
      bm.addBits(0, Constants.FILE_LENGTH_BITS);
    } 
    else {
      String stopWordFilePath = stopWordFile.getAbsolutePath();
      bm.addBits(stopWordFilePath.length(), Constants.FILE_LENGTH_BITS);
      for (int k = 0; k < stopWordFilePath.length(); k++) {
        bm.addBits(stopWordFilePath.charAt(k), Constants.BITS_PER_ASCII_BYTE);
      }
    }

    /* Add # of input files */
    bm.addBits(filesToIndex.size(), Constants.NUM_INPUT_FILES_BITS);

    for (int i = 0; i < filesToIndex.size(); i++) {
      IndexedFile inputFile = (IndexedFile) (filesToIndex.get(i));
      WordHistogram histogram = new WordHistogram(breakChars, stopWordFile, inputFile.getFile());
      Map wordMap = histogram.getTrimmedHistogram(.1, .1);
      Iterator itr = wordMap.keySet().iterator();
      while (itr.hasNext()) {
        Object word = itr.next();
        inputFile.addWordToBitmask(word.toString().toLowerCase());
      }

      /* Add file name length and file name */
      String filePath = inputFile.getFile().getAbsolutePath();
      bm.addBits(filePath.length(), Constants.FILE_LENGTH_BITS);
      for (int a = 0; a < filePath.length(); a++) {
        bm.addBits(filePath.charAt(a), Constants.BITS_PER_ASCII_BYTE);
      }

      /* Add # chars in file */
      bm.addBits(inputFile.getCharCount(), Constants.FILE_CHARS_BITS);

      /* Add # words in file */
      bm.addBits(inputFile.getWordCount(), Constants.FILE_WORDS_BITS);

      /* Add # of bits for this file's filter */
      long fileSize = inputFile.getFile().length() * 8; //*8 to convert to bits
      long filterLen = (long) (fileSize * Constants.PCT_OF_FILE_SIZE); //num of bits used to store the filter for this file
      bm.addBits(filterLen, 7);

      /* Add filter */
      bm.addBits(inputFile.getBitmask(), (int)filterLen);
    }
    return bm.save(Constants.INDEX_FILE);
  }
}